﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureProfileInstanceUtils.h"
#include "AlgorithmCurveOperate.h"
#include "AlgorithmIntersect.h"
#include "AlgorithmPolygonOperate.h"
#include "AlgorithmProject.h"
#include "EnLoopsState.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "GraphicsNodeUtils.h"
#include "IBaseGraphicsElementShapeComponent.h"
#include "IDocument.h"
#include "IElementBasicInformation.h"
#include "IElementParameters.h"
#include "IElementPosition.h"
#include "IElementPositionReportComponent.h"
#include "IGraphicsElementShape.h"
#include "IInstance.h"
#include "ILine3d.h"
#include "IModelLine.h"
#include "IOpeningGraphicsElementShapeComponent.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IParameterValueCurveByPoints.h"
#include "IPlane.h"
#include "IPlaneCurve3d.h"
#include "IPositionAssociatedPlane.h"
#include "IStructureSlopeLine.h"
#include "IType.h"
#include "ProfileUsedModelLineUtils.h"
#include "StructureProfileInstanceInput.h"
#include "Vector2dUtils.h"
#include "IGeometryRelationshipComponent.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

CREATE_DEBUG_MODE(UseProfileCoordinateSystemBuiltInParameterToTransmitProfile, L"使用新的坐标系参数传递Instance轮廓", gcmp::DebugModeGroup::DMGT_FAMILY_SERVICE, L"GDMPLab", L"2023-12-20",
    L"打开这个调试标注位，使用新增的ProfileCoordinateSystemBuiltInParameter内置参数在族服务中传输轮廓。");

using namespace gcmp;

namespace
{
    gcmp::Coordinate3d GetCoordinate3d(const IDocument* pDoc, const ElementId& id)
    {
        Coordinate3d res;
        DBG_WARN_AND_RETURN_UNLESS(pDoc, res, L"输入无效参数pDoc",L"GDMPLab",L"2024-03-30");

        if (!id.IsValid())
            return res;

        const IElement* element = pDoc->GetElement(id);
        DBG_WARN_AND_RETURN_UNLESS(element, res, L"获取Element失败",L"GDMPLab",L"2024-03-30");

        const IElementPositionReportComponent* pPositionReport = element->GetElementPositionReportComponent();
        if (pPositionReport != nullptr && pPositionReport->GetCoordinate3d(res))
        {
            return res;
        }
        return res;
    }

    EnLoopsState GenMergedPolygon(std::vector<OwnerPtr<gcmp::ICurve2d>> &curve2dlist, gcmp::OwnerPtr<gcmp::IPolygon>& polygon, std::vector<Int32>& curveIndices)
    {
        polygon = IPolygon::Create();
        std::vector<gcmp::OwnerPtr<gcmp::IPolygon>> polygonList;
        std::map<std::tuple<int, int, int>, int> tmpPolygonLoopCoedgeIndexToOriginCrvIndexMap;
        std::vector<const gcmp::ICurve2d*> curve2dlistNew;
        FOR_EACH(pGCurve2d, curve2dlist)
            curve2dlistNew.push_back(pGCurve2d.get());
        std::vector<const gcmp::ICurve2d*> errorCurves;
        gcmp::EnLoopsState loopsState = AlgorithmCurveOperate::Curve2dlistToPolygonLists(curve2dlistNew, polygonList, tmpPolygonLoopCoedgeIndexToOriginCrvIndexMap, gcmp::Constants::LENGTH_EPS, errorCurves);

        //释放polygonlist
        int polygonCount = 0;
        int allLoopCount = 0;
        std::vector<gcmp::OwnerPtr<gcmp::IPolygon> >::iterator polygonIter;
        std::map<std::tuple<int, int, int>, int> resultPolygonLoopCoedgeIndexToOriginCrvIndexMap;
        for (polygonIter = polygonList.begin(); polygonIter != polygonList.end();)
        {
            int loopCount = (*polygonIter)->GetPolyCurveCount();
            AlgorithmPolygonOperate::Merge(polygon.get(), (*polygonIter).get(), polygon);

            polygonIter = polygonList.erase(polygonIter);

            for (auto it = tmpPolygonLoopCoedgeIndexToOriginCrvIndexMap.begin(); it != tmpPolygonLoopCoedgeIndexToOriginCrvIndexMap.end(); ++it)
            {
                int polygonIndex = std::get<0>(it->first);
                int loopIndex = std::get<1>(it->first);
                int coedgeIndex = std::get<2>(it->first);
                int profileCrvIndex = it->second;

                if (polygonCount == polygonIndex)
                {
                    int newLoopIndex = allLoopCount + loopIndex;
                    resultPolygonLoopCoedgeIndexToOriginCrvIndexMap.insert(std::make_pair(std::tuple<int, int, int>(0, newLoopIndex, coedgeIndex), profileCrvIndex));
                }
            }

            polygonCount++;
            allLoopCount += loopCount;
        }

        for (auto it = resultPolygonLoopCoedgeIndexToOriginCrvIndexMap.begin();
            it != resultPolygonLoopCoedgeIndexToOriginCrvIndexMap.end(); ++it)
        {
            curveIndices.push_back(it->second);
        }

        polygon->SetDistEpsilon(Constants::LENGTH_EPS);
        return loopsState;
    }
}
//计算polygon
OwnerPtr<IPolygon> StructureProfileInstanceUtils::CalculateProfilePolygon(const std::vector<const ICurve3d*>& profiles, const Coordinate3d& baseCoord)
{
    std::vector<OwnerPtr<ICurve2d>> curve2dlist;
    OwnerPtr<IPlane> opPlane = IPlane::Create(baseCoord.GetOrigin(), baseCoord.GetX(), baseCoord.GetY());
    FOR_EACH(pCurve, profiles)
    {
        OwnerPtr<ICurve2d> opCurve2d = AlgorithmProject::Project(pCurve, opPlane.get());
        curve2dlist.emplace_back(TransferOwnership(opCurve2d));
    }
    OwnerPtr<IPolygon> opPolygon;
    std::vector<Int32> indices;
    //
    EnLoopsState eRes = GenMergedPolygon(curve2dlist, opPolygon, indices);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(eRes == EnLoopsState::LOOPVALID, L"无效轮廓",L"GDMPLab",L"2024-03-30");
    return opPolygon;
}

bool StructureProfileInstanceUtils::CalculateFloorScratchHeight(const IInstance* pInstance, const std::vector<IModelLine*>& profileCurves,const IStructureSlopeLine* pSlopeLine, double & compThickness)
{
    // 厚度
    if (pInstance == nullptr)
        return false;

    const IElementBasicInformation* pElementBasicIfmt = pInstance->GetBasicInformation();
    if (pElementBasicIfmt == nullptr)
        return false;

    const IType* pType = pElementBasicIfmt->GetType();
    if (pType == nullptr)
        return false;

    const IElementParameters* pParameters = pType->GetElementParameters();
    if (pType == nullptr)
        return false;
    
    OwnerPtr<IParameter> paramW = pParameters->GetParameterByUid(PARAMETER_UID(FloorCoreThicknessBuiltInParameter));
    DBG_WARN_AND_RETURN_UNLESS(IParameter::IsValidParameter(paramW), 0.0, L"没楼板的厚度参数",L"GDMPLab",L"2024-03-30");
    double thickness = paramW->GetValueAsDouble();
    double offset = 0.0;
    IPositionAssociatedPlane::GetBottomPlaneHeightOffset(pInstance, offset);

    if (pSlopeLine)
    {
        // 定位
        OwnerPtr<ILine3d> opSlopeLine = pSlopeLine->GetSlopeLine();

        Vector3d topCenterPt = opSlopeLine->GetStartPoint();
        Vector3d normal = Vector3d::UnitZ;
        // 坡度
        Vector3d slopAxis = opSlopeLine->GetDirection().Cross(Vector3d::UnitZ);
        normal = slopAxis.Cross(opSlopeLine->GetDirection());
        normal.Normalize();

        Vector3d bottomCenterPt = topCenterPt - normal * thickness;
        // 根据定位、厚度和坡度，计算顶底切平面
        OwnerPtr<IPlane> opTopPlane = IPlane::Create(topCenterPt, normal);
        OwnerPtr<IPlane> opBottomPlane = IPlane::Create(bottomCenterPt, -normal);

        double maxdistance = 0.0;
        FOR_EACH(pModelLine, profileCurves)
        {
            Vector3d  startPt = pModelLine->GetGeometryCurve()->GetStartPoint();
            OwnerPtr<ILine3d> opline = ILine3d::Create(startPt, Vector3d::UnitZ, Intervald(-Constants::MAX_MODEL_SIZE / 2.0, Constants::MAX_MODEL_SIZE / 2.0));

            std::vector<OwnerPtr<ICurve3dSurfaceIntersection>> opIntersectsTop, opIntersectsBottom;
            AlgorithmIntersect::Intersect(opline.get(), opTopPlane.get(), opIntersectsTop, Constants::LENGTH_EPS);
            AlgorithmIntersect::Intersect(opline.get(), opBottomPlane.get(), opIntersectsBottom, Constants::LENGTH_EPS);
            if (opIntersectsTop.size() != 1 || opIntersectsBottom.size() != 1)
            {
                return false;
            }
            Vector3d intersectPtTop = opline->GetPoint(opIntersectsTop.front()->GetParamOnCurve());
            Vector3d intersectPtBottom = opline->GetPoint(opIntersectsBottom.front()->GetParamOnCurve());

            double toTopDist = (intersectPtTop - startPt).Length();
            double toBottomDist = (intersectPtBottom - startPt).Length();

            double maxDis = toTopDist > toBottomDist ? toTopDist : toBottomDist;
            DBG_WARN_AND_RETURN_UNLESS(maxDis > 0, 0.0, L"楼板的数据计算错误！",L"GDMPLab",L"2024-03-30");
            if (maxDis > maxdistance)
            {
                maxdistance = maxDis;
            }
        }
        compThickness = maxdistance + 2 * thickness + 2 * offset;
    }
    else
    {
        compThickness = 2 * thickness + 2 * offset;
    }
    return true;
}

bool StructureProfileInstanceUtils::SetFloorCompensationThickness(IInstance* pInstance, double compensationThickness)
{
    OwnerPtr<IParameter> paramW = ELEMENT_PARAMETER(pInstance, FloorCompensationThicknessBuiltInParameter);
    DBG_WARN_AND_RETURN_UNLESS(IParameter::IsValidParameter(paramW), nullptr, L"没楼板的厚度参数",L"GDMPLab",L"2024-03-30");

    paramW->SetValueAsDouble(compensationThickness);
    pInstance->GetElementParameters()->SetParameter(paramW.get());

    return true;
}

bool gcmp::StructureProfileInstanceUtils::SetProfileByCurveArray(const IPolygon*pPolygon, const Coordinate3d& baseCoord, gcmp::IInstance* pInstance)
{
    DBG_WARN_AND_RETURN_UNLESS(pPolygon, false, L"输入参数为空",L"GDMPLab",L"2024-03-30");
    // 更新轮廓曲线
    OwnerPtr<IParameter> refCoordinateSystems;
    if (DEBUG_MODE(UseProfileCoordinateSystemBuiltInParameterToTransmitProfile))
    {
        refCoordinateSystems = ELEMENT_PARAMETER(pInstance, ProfileCoordinateSystemBuiltInParameter);
    }
    else
    {
        refCoordinateSystems = ELEMENT_PARAMETER(pInstance, CoordinateSystemBuiltInParameter);
    }
    DBG_WARN_AND_RETURN_UNLESS(IParameter::IsValidParameter(refCoordinateSystems), false, L"pInstance中没有预期参数",L"GDMPLab",L"2024-03-30");
    IParameterValueCurveByPoints* pPVSCoordinate = 
        dynamic_cast<IParameterValueCurveByPoints*>(refCoordinateSystems->GetParameterValueStorageFw());
    DBG_WARN_AND_RETURN_UNLESS(pPVSCoordinate != nullptr, false, L"pInstance中没有预期参数",L"GDMPLab",L"2024-03-30");
    bool isSuccessful = pPVSCoordinate->SetCoordinateSystemsByPolygon(pPolygon);
    DBG_WARN_AND_RETURN_UNLESS(isSuccessful, false, L"更新轮廓曲线失败",L"GDMPLab",L"2024-03-30");
    pInstance->GetElementParameters()->SetParameter(refCoordinateSystems.get());
    return true;
}

bool gcmp::StructureProfileInstanceUtils::GenerateProfileCurvesFromPolygon(IDocument* pDoc, const IPolygon* pPolygon, const ElementId& associatedPlaneId,
    std::vector<IModelLine*>& profileCurves)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pPolygon, L"pPolygon为空",L"GDMPLab",L"2024-03-30");

    // 生成轮廓线集合
    Coordinate3d planeCoord = GetCoordinate3d(pDoc, associatedPlaneId);
    int polyCurveNum = pPolygon->GetPolyCurveCount();
    for (int i = 0; i < polyCurveNum; i++)
    {
        OwnerPtr<IPolyCurve> opPolyCurve = pPolygon->GetPolyCurve(i);
        std::vector<OwnerPtr<ICurve2d>> curve2dList;
        bool bOk = opPolyCurve->GetAllCurves(curve2dList);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(bOk, L"获取curve2dList失败",L"GDMPLab",L"2024-03-30");

        FOR_EACH(curve2d, curve2dList)
        {
            OwnerPtr<IPlaneCurve3d> opCurve3d = IPlaneCurve3d::Create(planeCoord, TransferOwnership(curve2d));
            DBG_WARN_AND_RETURN_FALSE_UNLESS(opCurve3d, L"opCurve3d为空",L"GDMPLab",L"2024-03-30");

            IModelLine* pModelLine= gcmp::ProfileUsedModelLineUtils::CreateModelLineUsedForProfile(pDoc, TransferOwnership(opCurve3d));
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelLine, L"pModelLine为空",L"GDMPLab",L"2024-03-30");
            profileCurves.push_back(pModelLine);
        }
    }
    DBG_WARN_AND_RETURN_FALSE_UNLESS(!profileCurves.empty(), L"生成轮廓线集合失败",L"GDMPLab",L"2024-03-30");

    return true;
}

bool gcmp::StructureProfileInstanceUtils::GeneratePolygonFromProfileCurves(const std::vector<IModelLine*>& profileCurves, OwnerPtr<IPolygon>& opPolygon, Coordinate3d& polygonCoord)
{
    std::vector<OwnerPtr<ICurve3d>> profileICurvesOwner;
    std::vector<const ICurve3d*> profileGraphicsCurves;
    std::vector<OwnerPtr<ICurve3d>> outerCurves;           // 外圈轮廓
    std::vector<int> outerIndexes;

    FOR_EACH(pModelLine, profileCurves)
    {
        OwnerPtr<ICurve3d> opCurve3d = pModelLine->GetGeometryCurve();
        profileGraphicsCurves.push_back(opCurve3d.get());
        profileICurvesOwner.push_back(TransferOwnership(opCurve3d));
    }
    gcmp::AlgorithmCurveOperate::CreateSequencedCurves(profileGraphicsCurves, outerCurves, outerIndexes);

    // 计算一个插值点
    Vector3d insertPt = Vector3d::Zero;
    {
        Box3d box;
        FOR_EACH(curve, outerCurves)
        {
            box.MergeBox(curve->GetBox());
        }

        insertPt = box.GetCenter();
    }

    // 以插入点为原点坐标系
    Coordinate3d baseCoord(insertPt, Vector3d::UnitX, Vector3d::UnitY);
    OwnerPtr<IPolygon> opProfilePolygon = StructureProfileInstanceUtils::CalculateProfilePolygon(profileGraphicsCurves, baseCoord);
    opPolygon = TransferOwnership(opProfilePolygon);
    polygonCoord = baseCoord;

    return true;
}

IStructureSlopeLine* gcmp::StructureProfileInstanceUtils::GenerateStructureFloorSlopeLine(IDocument* pDoc, const StructureFloorSlopeLineInput* pSlopeLineInput)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pSlopeLineInput, L"pSlopeLineInput为空",L"GDMPLab",L"2024-03-30");

    double slopeAngle = pSlopeLineInput->SlopeAngle;
    double endElevationOffset = pSlopeLineInput->EndElevationOffset;
    double startElevationOffset = pSlopeLineInput->StartElevationOffset;

    if (StructureSlopeDefinitionType::SlopeAngle == pSlopeLineInput->DefinitionType)
    {
        bool bOk = Intervald(-89.99, 89.99).Contain(MathUtils::RadiansToAngle(slopeAngle));
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"坡度角范围应在[-89.99, 89.99]之间",L"GDMPLab",L"2024-03-30");

        double slopeHorizontalLength = Vector2dUtils::Distance(pSlopeLineInput->StartPoint, pSlopeLineInput->EndPoint);
        double trans = slopeHorizontalLength * std::tan(pSlopeLineInput->SlopeAngle);
        endElevationOffset = startElevationOffset + trans;
    }

    IStructureSlopeLine* pSlopeLine = IStructureSlopeLine::Create(pDoc, Vector3d(pSlopeLineInput->StartPoint, 0),
        Vector3d(pSlopeLineInput->EndPoint, 0));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pSlopeLine, L"未能成功生成楼板坡度线",L"GDMPLab",L"2024-03-30");
    pSlopeLine->SetStartElevationOffset(startElevationOffset);
    pSlopeLine->SetEndElevationOffset(endElevationOffset);

    return pSlopeLine;
}

// 新的结构构件不要使用 InstanceUtils::ValidateFloorHoles(const IInstance* pFloor, const IInstance* pHole, std::wstring& errorMessage)
// 应该使用这里的代码
bool StructureProfileInstanceUtils::ValidatePlateHoles(const IInstance* pPlate, const IInstance* pHole, std::wstring& errorMessage)
{
    // 洞口的CutterGrep与板的BaseGrep相交
    const IBaseGraphicsElementShapeComponent* pPlateShapeComponent = pPlate->GetBaseGraphicsElementShapeComponent();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pPlateShapeComponent, L"pPlateShapeComponent为空",L"GDMPLab",L"2024-03-30");

    const IGraphicsElementShape* plateBaseGrep = pPlateShapeComponent->GetBaseGraphicsElementShape();

    OwnerPtr<IGraphicsElementShape> opClonedCutterGRep = nullptr;
    const IGeometryRelationshipComponent* pGeometryRepCpt = pHole->GetGeometryRelationshipComponent();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pGeometryRepCpt != nullptr, L"无效的指针",L"GDMPLab",L"2024-03-30");
    const IOpeningGraphicsElementShapeComponent* pCutterGRepComponent = pGeometryRepCpt->GetOpeningGraphicsElementShapeComponent();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pCutterGRepComponent != nullptr, L"无效的指针",L"GDMPLab",L"2024-03-30");
    const IGraphicsElementShape* pCutterGRep = pCutterGRepComponent->GetOpeningGraphicsElementShape();
    if (!pCutterGRep)
    {
        return true;
    }

    opClonedCutterGRep = TransferOwnershipCast<IGraphicsElementShape>(pCutterGRep->Clone());
    const IElementPosition *posBehavior = pHole->GetElementPosition();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(posBehavior, L"无效的指针posBehavior",L"GDMPLab",L"2024-03-30");
    Matrix4d instanceTrans = posBehavior->ComputeLocalToWorldCoordinateSystemTransformMatrix();
    if (!instanceTrans.IsIdentity())
    {
        opClonedCutterGRep->Transform(instanceTrans);
    }

    //如果使用GRep做位置关系判断有性能问题，可改为投影判断(因为有变截面和弧形墙代码会变得非常复杂)
    //理论上洞口在建筑大模型中所占比例应该不会特别大(主要是墙梁板柱)，这个根据实测数据来判断
    BodiesPositionTypes positionType = GraphicsNodeUtils::GetBodiesPositionType(pPlate->GetDocument(), plateBaseGrep, opClonedCutterGRep.get());
    if (positionType != BodiesPositionTypes::Separation)
    {
        return true;
    }
    errorMessage = GBMP_TR(L"不能完全超出板边界！");
    return false;
}

void gcmp::StructureProfileInstanceUtils::MirrorProfileCoordinate(const IInstance * pInstance, std::vector<OwnerPtr<ICurve3d>>& profileICurvesOwner)
{
    // 该段代码由 StructureFloor::Edit(const StructureProfileInstanceEditElementInput* pInput) 转移到此处
    // ****************************************需要注意**************************************************
    // 出错率99%，改动慎重！
    // 如果Instance是镜像的，它的LcsToWcsTransformMatrix中是含有镜像信息的，这边需要做处理。
    OwnerPtr<IParameter> mirrorParam = ELEMENT_PARAMETER(pInstance, InstanceMirrorSignBuiltInParameter);
    bool isMirror = IParameter::IsValidParameter(mirrorParam) && mirrorParam->GetValueAsBool();
    if (isMirror)
    {
        //为什么需要这一步？
        //因为镜像后实体坐标系变成了(-X,-Y), 因此需要将看到的(X,Y)坐标系下的轮廓线变换到实体坐标系上
        const IElementPosition* posBehavior = pInstance->GetElementPosition();
        DBG_WARN_AND_RETURN_VOID_UNLESS(posBehavior, L"posBehavior为空",L"GDMPLab",L"2024-03-30");
        Matrix4d mat = posBehavior->ComputeLocalToWorldCoordinateSystemTransformMatrix();
        Matrix4d matInverse;
        bool bOk = mat.GetInverse(matInverse);
        DBG_WARN_AND_RETURN_VOID_UNLESS(bOk, L"GetInverse失败",L"GDMPLab",L"2024-03-30");

        Matrix4d matMirror;
        matMirror.MakeMirror(Vector3d::Zero, Vector3d::UnitY);
        FOR_EACH(pProfileCurve, profileICurvesOwner)
        {
            // 世界坐标系变回局部坐标系
            pProfileCurve->Transform(matInverse);
            // 在局部坐标系将轮廓镜像
            pProfileCurve->Transform(matMirror);
            // 最后从局部坐标系再变换回世界坐标系
            pProfileCurve->Transform(mat);
        }
    }
}
